package com.fmy.mcsecurity.config.filter;


import cn.hutool.core.util.StrUtil;
import com.fmy.mcsecurity.commons.resp.SecurityResp;
import com.fmy.mcsecurity.commons.unusual.McSecurityException;
import com.fmy.mcsecurity.commons.unusual.TypeIntegrationException;
import com.fmy.mcsecurity.integration.ClientSupportIntegration;
import com.fmy.mcsecurity.integration.TypeIntegration;
import com.fmy.mcsecurity.integration.TypeIntegrationHolder;
import com.fmy.mcsecurity.login.hander.LoginHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * @author Mr.fmy
 * @version 1.0.0
 * @ClassName UsernamePassParamsFilter
 * @Description TODO 用户名 密码 登录时多余的参数填充
 * <p>
 * 其中 type 参数 和 client 参数 可以在请求头中赋值
 * </p>
 * @createTime 2019/10/23 11:48
 */
@Slf4j
public class UsernamePassParamsFilter extends OncePerRequestFilter {

    private Set<String> paramNames = new HashSet<>();
    private OrRequestMatcher matcher = null;
    private LoginHandler loginHandler;
    private ClientSupportIntegration clientSupportIntegration;
    private final static String typeKey = "type";
    private final static String clientKey = "client";
    private final static String usernameKsy = "username";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        if (requiresAuthentication(request)) {
            try {
                //1、填充参数
                setParams(request);
                //2、客户端是否支持
                supperClient();
                //3、登录方式预处理
                prepare();
                doFilter(request, response, chain);
            } catch (Exception e) {
                if (e instanceof McSecurityException) {
                    loginHandler.paramsFail(response, ((McSecurityException) e).getiEnum(), e);
                    return;
                } else {
                    log.error(e.getMessage(), e);
                }
                loginHandler.paramsFail(response, SecurityResp.PARAM_FAIL_ERROR, e);
            } finally {
                UsernamePassParamsHolder.clear();
            }
        } else {
            doFilter(request, response, chain);
        }
    }


    public void setMatcher(AntPathRequestMatcher... matcher) {
        Assert.notEmpty(matcher, "matcher must not be null");
        this.matcher = new OrRequestMatcher(matcher);
    }

    private boolean requiresAuthentication(HttpServletRequest request) {
        if (matcher == null) {
            throw new IllegalArgumentException("请配置 UsernamePassParamsFilter 拦截路径，该路径一般就是登录路径");
        }
        return matcher.matches(request);
    }

    public void setParamNames(String... paramNames) {
        Assert.notEmpty(paramNames, "paramNames must not be null");
        this.paramNames.addAll(Arrays.asList(paramNames));
    }

    /**
     * 填充参数
     */
    private void setParams(HttpServletRequest request) {
        Map<String, Object> params = new HashMap<>(paramNames.size());
        paramNames.forEach(param -> params.put(param, request.getParameter(param)));

        String typeV = request.getParameter(typeKey);
        String clientV = request.getParameter(clientKey);
        String usernameV = request.getParameter(usernameKsy);

        if (StrUtil.isBlank(typeV)) {
            typeV = request.getHeader(typeKey);
        }
        if (StrUtil.isBlank(clientV)) {
            clientV = request.getHeader(clientKey);
        }

        if (StrUtil.isBlank(typeV)) {
            throw new TypeIntegrationException("请传入" + typeKey + "参数，该参数可携带在header或请求参数中", SecurityResp.PARAM_FAIL_ERROR);
        }

        if (StrUtil.isBlank(clientV)) {
            throw new TypeIntegrationException("请传入" + clientKey + "参数，该参数可携带在header或请求参数中", SecurityResp.PARAM_FAIL_ERROR);
        }

        params.put(typeKey, typeV);
        params.put(clientKey, clientV);
        params.put(usernameKsy, usernameV);

        UsernamePassParamsHolder.set(params);
    }

    /**
     * 效验客户端
     */
    private void supperClient() {
        if (!clientSupportIntegration.support(UsernamePassParamsHolder.getClient())) {
            throw new TypeIntegrationException(SecurityResp.PARAM_FAIL_NOT_CLIENT_ERROR);
        }
    }

    /**
     * 进行预处理
     */
    private void prepare() {
        String type = UsernamePassParamsHolder.getLoginType();
        boolean isIntegration = false;

        for (TypeIntegration integration : TypeIntegrationHolder.getAuthenticators()) {
            if (integration.support(type)) {
                integration.before(UsernamePassParamsHolder.get());
                isIntegration = true;
            }
        }

        if (!isIntegration && StrUtil.isNotBlank(type)) {
            throw new TypeIntegrationException(SecurityResp.PARAM_FAIL_TYPE_ERROR);
        }
    }

    public static String getTypeKey() {
        return typeKey;
    }

    public static String getClientKey() {
        return clientKey;
    }

    public static String getUsername() {
        return usernameKsy;
    }

    public void setLoginHandler(LoginHandler loginHandler) {
        Assert.notNull(loginHandler, "Please configure loginhandler");
        this.loginHandler = loginHandler;
    }

    public void setClientSupportIntegration(ClientSupportIntegration clientSupportIntegration) {
        Assert.notNull(clientSupportIntegration, "Please configure clientsupportintegration");
        this.clientSupportIntegration = clientSupportIntegration;
    }
}
